using System;
using System.Collections.Generic;
using System.Text;
using MSRI.SmsService;
using System.Threading;
using System.Xml;
using Microsoft.Practices.EnterpriseLibrary.Data;
using MSRI.HelperClasses;
using Instedd.Labs.Geo.Library.Kml;
using Instedd.Labs.Geo.Library.GPS;
using Instedd.Labs.Geo.Library.Rss;


namespace Instedd.Labs.Geo.Library.Sms
{
    public enum SmsManagerType { SendReceiveManager, SendOnlyManager }

    public class SmsManager
    {
        static SmsManager instance;

        KmlManager kmlManager;
        RssManager geoRssManager;
        SmsMessagesManager smsMessagesManager;
        SmsServiceClass smsService;
        ISenderNameLookupProvider senderNameLookupProvider;
        GoogleGeoCoder geoCoder;

        Configuration configuration = new Configuration();

        private SmsManager()
        {
            this.configuration.Load(System.Configuration.ConfigurationManager.AppSettings);
        }

        internal SmsManager(string smsServiceName)
            : this()
        {
            Guard.ArgumentNotNullOrEmptyString(smsServiceName, "smsServiceName");

            StartSmsService(smsServiceName);
        }

        internal SmsManager(SmsServiceClass smsService)
            : this()
        {
            Guard.ArgumentNotNull(smsService, "smsService");

            StartSmsService(smsService);
        }

        public static SmsManager Start(string smsServiceName)
        {
            return Start(smsServiceName, SmsManagerType.SendReceiveManager);
        }

        public static SmsManager Start(string smsServiceName, SmsManagerType type)
        {
            if (instance != null)
            {
                // TODO: Move to resources.
                throw new InvalidOperationException("The Sms Manager can't be started because there is an existent manager instance running.");
            }

            if (type == SmsManagerType.SendReceiveManager)
            {
                instance = new SmsManager(smsServiceName);
            }
            else
            {
                instance = new SendOnlySmsManager(smsServiceName);
            }
            return instance;
        }

        public static SmsManager GetManager()
        {
            return instance;
        }

        public static void Stop()
        {
            if (instance == null)
            {
                throw new InvalidOperationException("There isn't any SMS Manager instance running to stop.");
            }

            instance.smsService.OnDeliveryReportReceived -= instance.smsService_OnDeliveryReportReceived;
            instance.smsService.OnSmsReceived -= instance.smsService_OnSmsReceived;

            instance = null;
        }

        private void StartSmsService(string smsServiceName)
        {
            StartSmsService(new SmsServiceClass(smsServiceName));
        }

        private void StartSmsService(SmsServiceClass smsService)
        {
            this.smsService = smsService;

            InitializeSmsService();
        }

        protected virtual void InitializeSmsService()
        {
            smsService.OnDeliveryReportReceived += new OnDeliveryReportDelegate(smsService_OnDeliveryReportReceived);
            smsService.OnSmsReceived += new OnSmsReceivedDelegate(smsService_OnSmsReceived);
        }

        public void GenerateKml(KmlFactorySettings settings, string path)
        {
            // TODO: Check how the kml file should be created. 
            using (XmlWriter writer = XmlWriter.Create(path))
            {
                // TODO: Check which message should be used to generate the kml file.
                KmlFormat KmlFormat = KmlManager.CreateKml(this.MessagesManager.GetAllMessages(), settings);
                KmlManager.KmlWriter.WriteKml(writer, KmlFormat);
            }
        }

        public void GenerateGeoRss(RssFactorySettings settings, string path)
        {
            using (XmlWriter writer = XmlWriter.Create(path))
            {
                GenerateGeoRss(writer, this.MessagesManager.GetAllMessages(), settings);
            }
        }

        public void GenerateGeoRssByUser(string senderNumber)
        {
            GenerateGeoRssByUser(RssFactorySettings.Default, FileNamesGenerator.GenerateRssFileName(configuration, this.senderNameLookupProvider.LookupSenderName(senderNumber)), senderNumber);
        }

        public void GenerateGeoRssByUser(RssFactorySettings settings, string path, string senderNumber)
        {
            using (XmlWriter writer = XmlWriter.Create(path))
            {
                GenerateGeoRss(writer, this.MessagesManager.GetMessagesBySenderNumber(senderNumber), settings);
            }
        }

        private void GenerateGeoRss(XmlWriter xmlWriter, IEnumerable<SmsExtension> messages, RssFactorySettings settings)
        {
            RssWriter rssWriter = new RssWriter();
            RssFormat rss = this.GeoRssManager.CreateGeoRss(messages, settings);
            rssWriter.WriteRss(xmlWriter, rss);
        }

        void smsService_OnDeliveryReportReceived(DeliveryReportClass DeliveryReport)
        {
        }

        SmsResponseClass smsService_OnSmsReceived(SmsClass sms)
        {
            try
            {
                SmsExtension smsExtension = this.MessagesManager.Create(sms);
                this.smsMessagesManager.Save(smsExtension);

                GenerateKml();
                GenerateGeoRss();
                GenerateGeoRssByUser(smsExtension.Sms.Number);
            }
            catch (InvalidBodyFormatException ex)
            {
                // TODO: The incomming SMS message is invalid. Log the invalid body format exception.
            }

            return null;
        }

        public void GenerateKml()
        {
            GenerateKml(KmlFactorySettings.Default, FileNamesGenerator.GenerateKmlFileName(Configuration));
            GenerateKml(new KmlFactorySettings(false, true), FileNamesGenerator.GenerateKmlFileNameForSharing(Configuration));
        }

        public void GenerateGeoRss()
        {
            GenerateGeoRss(RssFactorySettings.Default, FileNamesGenerator.GenerateRssFileName(Configuration));
            GenerateGeoRss(new RssFactorySettings(false, true), FileNamesGenerator.GenerateRssFileNameForSharing(Configuration));
        }

        public KmlManager KmlManager
        {
            get { return FactoryUtil.CreateIfItIsNull(ref kmlManager); }
        }

        public SmsMessagesManager MessagesManager
        {
            get
            {
                return FactoryUtil.CreateIfItIsNull(ref smsMessagesManager,
              delegate() { return new SmsMessagesManager(DatabaseFactory.CreateDatabase("SMS.VE.MessagesDatabase"), this.SenderNameLookupProvider, this.GeoCoder, this.Configuration); });
            }
        }

        public RssManager GeoRssManager
            {
                get { return FactoryUtil.CreateIfItIsNull(ref geoRssManager); }
        }

        public SmsServiceClass Service
        {
            get { return this.smsService; }
        }

        public ISenderNameLookupProvider SenderNameLookupProvider
        {
            get
            {
                return FactoryUtil.CreateIfItIsNull(ref this.senderNameLookupProvider,
              delegate() { return new CsvSenderNameLookupProvider(PathResolver.TryToResolveUsingDataDirectory(Configuration.SenderNameLookupPath)); });
            }
        }

        public IGeoCoder GeoCoder
        {
            get { return FactoryUtil.CreateIfItIsNull(ref geoCoder); }
        }

        public Configuration Configuration
        {
            get { return configuration; }
            set { configuration = value; }
        }

        internal class SendOnlySmsManager : SmsManager
        {
            internal SendOnlySmsManager(string smsServiceName) : base(smsServiceName) { }

            internal SendOnlySmsManager(SmsServiceClass smsService) : base(smsService) { }

            protected override void InitializeSmsService() { }
        }
    }
}